package com.lp.multi.lock.type;

import java.util.concurrent.atomic.AtomicReference;

/**
 * @author: lp
 * @create: 2021-06-29 21:29
 * @description: 自旋锁自己实现(不可重入)
 * 自旋锁其他种类：TicketLock 、CLHlock 、MCSlock
 * http://ifeve.com/java_lock_see2/
 *
 * 锁分类:
 * 线程要不要锁住资源:1.乐观锁(不锁) 2.悲观锁(锁)
 *
 * 多个线程能不能共享一把锁:1.独享锁(ReentrantLock写锁) 2.共享锁(ReentrantLock读锁)
 *
 * 多个线程争抢锁，能不能插队：1.公平锁(排队，不能插队) 2.非公平锁(先尝试插队，插队失败排队)
 *
 * 互斥锁、读写锁
 *
 * 一个线程能不能多次获取同一把锁：1.可重入锁(可多次获得) 2.不可重入锁(不能多次获得)
 *
 * 锁住同步资源失败，线程要不要阻塞：1.阻塞锁  2.不阻塞锁(自旋锁、适应性自旋锁)
 *
 * 多个线程竞争同步资源的流程细节有没有区别：
 *   1.不锁资源，多个线程中只能有一个线程修改资源成功，其他线程会重试(无锁)
 *   2.同一个线程执行同步资源时自动获取资源(偏向锁)
 *   3.多个线程竞争同步资源时，没有获取资源的线程自旋等待锁释放(轻量级锁)
 *   4.多个线程竞争同步资源时，没有获得资源的线程阻塞等待唤醒(重量级锁)
 *
 * 分段锁
 * 锁升级(无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁) JDK1.6
 *
 *
 *
 * 悲观锁：Synchronized、ReentrantLock写锁(独占锁/排它锁)
 *
 * 乐观锁：CAS(有ABA问题)、版本号机制(没有ABA问题)
 *
 * 更多锁资源
 * https://tech.meituan.com/2018/11/15/java-lock.html
 *
 */


/**
 * 自旋锁
 *
 * 自旋锁在JDK1.4.2中引入，使用-XX:+UseSpinning来开启，JDK 6中变为默认开启，并且引入了自适应的自旋锁（适应性自旋锁）
 * JDK自旋锁：
 * 自旋等待的时间必须要有一定的限度，如果自旋超过了限定次数（默认是10次，可以使用-XX:PreBlockSpin来更改）没有成功获得锁，就应当挂起线程
 */
public class SpinLock {
    private AtomicReference<Thread> sign =new AtomicReference<>();
    public void lock(){
        Thread current = Thread.currentThread();
        while(!sign .compareAndSet(null, current)){

        }
    }

    public void unlock (){
        Thread current = Thread.currentThread();
        sign .compareAndSet(current, null);
    }

}


/**
 * 不可重入锁验证
 * 打印结果：(程序会获取不到锁，不会停止)
 * Thread-0进入sendMsg方法准备获取锁
 * Thread-0进入sendMsg方法获取锁成功
 * Thread-0 send msg
 * Thread-0进入call方法准备获取锁
 */


class SpinLockTest1 implements Runnable {
    private SpinLock lock = new SpinLock();

    public static void main(String[] args) {
        new Thread(new SpinLockTest1()).start();
    }

    @Override
    public void run() {
        sendMsg();
    }

    public void sendMsg(){
        System.out.println(Thread.currentThread().getName()+"进入sendMsg方法准备获取锁");
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"进入sendMsg方法获取锁成功");
        try{
            System.out.println(Thread.currentThread().getName()+" send msg");
            call();
        }finally {
            lock.unlock();
        }

    }

    public void call(){
        System.out.println(Thread.currentThread().getName()+"进入call方法准备获取锁");
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"进入call方法获取锁成功");
        try{
            System.out.println(Thread.currentThread().getName()+" call");
        }finally {
            lock.unlock();
        }
    }
}


/**
 * 自旋可重入锁
 */
class SpinLock1 {
    private AtomicReference<Thread> owner =new AtomicReference<>();
    private int count =0;
    public void lock(){
        Thread current = Thread.currentThread();
        if(current==owner.get()) {
            count++;
            return ;
        }

        while(!owner.compareAndSet(null, current)){

        }
    }
    public void unlock (){
        Thread current = Thread.currentThread();
        if(current==owner.get()){
            if(count!=0){
                count--;
            }else{
                owner.compareAndSet(current, null);
            }
        }

    }
}





